<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // scroll 事件本身会触发页面的重新渲染,
        // 同时 scroll 事件的 handler 又会被高频度的触发,
        // 因此事件的 handler 内部不应该有复杂操作，例如 DOM 操作就不应该放在事件处理中。
        // 针对此类高频度触发事件问题（例如页面 scroll ，屏幕 resize，监听用户输入等），
        // 有两种常用的解决方法，防抖和节流。


        // 防抖实现
        // 简单例子: 防止鼠标连击 延迟hover
        // 当一次事件发生后，事件处理器要等一定阈值的时间，
        // 如果这段时间过去后 再也没有 事件发生，就处理最后一次发生的事件。
        // 假设还差 0.01 秒就到达指定时间，这时又来了一个事件，那么之前的等待作废，需要重新再等待指定时间。

        // 例如在搜索引擎搜索问题的时候，我们当然是希望用户输入完最后一个字才调用查询接口
        // ，这个时候适用延迟执行的防抖函数，它总是在一连串（间隔小于wait的）函数触发之后调用。
        // 例如用户给interviewMap点star的时候，我们希望用户点第一下的时候就去调用接口，
        // 并且成功之后改变star按钮的样子，用户就可以立马得到反馈是否star成功了，
        // 这个情况适用立即执行的防抖函数，它总是在第一次调用，并且下一次调用必须与前一次调用的时间间隔大于wait才会触发。


        // 这个是用来获取当前时间戳的
        function now() {
            return +new Date()
        }

        /**
         * 防抖函数,返回函数, 连续调用时,当空闲时间>wait 时,才会被调用
         * 适用于 搜索调用接口...
         */

        function debounce(fn, wait, immediate) {
            // immediate 为 true 时, 立即执行
            // false 时, 延迟函数中执行

            var context, args, timer;
            // 延迟函数
            function later() {
                return setTimeout(() => {
                    timer = null;
                    if (!immediate) {
                        fn.apply(context, args);
                        context = args = null;
                    }
                }, wait)
            }

            return function (...params) {
                if (!timer) {
                    timer = later();
                    if (immediate) {
                        fn.apply(this, args)
                    } else {
                        context = this;
                        args = params
                    }
                } else {
                    clearTimeout(timer);
                    timer = later();
                }
            }
        }

        /**
        * underscore 节流函数，返回函数连续调用时，func 执行频率限定为 次 / wait
        *
        * @param  {function}   func      回调函数
        * @param  {number}     wait      表示时间窗口的间隔
        * @param  {object}     options   如果想忽略开始函数的的调用，传入{leading: false}。
        *                                如果想忽略结尾函数的调用，传入{trailing: false}
        *                                两者不能共存，否则函数不能执行
        * @return {function}             返回客户调用函数
        */

        function throttle(func, wait, options = {}) {
            var context, args, result;
            var previous = 0;
 
            var later = function () {
                previous = options.leading ? 0 : Date.now();
                clearTimeout(timer)
                timer = null;
                func.apply(context, args)
                if (!timer) {
                    context = args = null;
                }
            }

            return function () {
                var now = Date.now();
                if (!previous && !options.leading) {
                    previous = Date.now();
                }
                var context = this;
                var args = arguments;
                var remain = wait - (now - previous);
                if (remain <= 0 || remain > wait) {
                    if (timer) {
                        clearTimeout(timer);
                        timer = null;
                    }
                    // 执行
                    previous = Date.now();
                    func.apply(context, args);
                    if (!timer) {
                        context = args = null;
                    }
                } else if (!timer && options.trailing) {
                
                    timer = setTimeout(later, remain)
                }
            }
        }
    </script>
</body>

</html>